// Copyright (c) 2022 wick.zt arg-go is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of
// the Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

package arg

import (
	"fmt"
	"reflect"
	"strconv"
	"time"
)

func parseValue(t reflect.Type, value string) (v interface{}, err error) {
	if value == "" {
		return nil, nil
	}

	var (
		val interface{}
	)
	switch t.Kind() {
	default:
		v, err = nil, &TypeError{UnknownType, t}

	case reflect.Bool:
		v, err = strconv.ParseBool(value)

	case reflect.Int:
		v, err = strconv.Atoi(value)
	case reflect.Int8:
		val, err = strconv.ParseInt(value, 10, 8)
		v = int8(val.(int64))
	case reflect.Int16:
		val, err = strconv.ParseInt(value, 10, 16)
		v = int16(val.(int64))
	case reflect.Int32:
		val, err = strconv.ParseInt(value, 10, 32)
		v = int32(val.(int64))
	case reflect.Int64:
		v, err = parseInt64(t, value)

	case reflect.Uint:
		val, err = strconv.ParseUint(value, 10, 64)
		v = uint(val.(uint64))
	case reflect.Uint8:
		val, err = strconv.ParseUint(value, 10, 8)
		v = uint8(val.(uint64))
	case reflect.Uint16:
		val, err = strconv.ParseUint(value, 10, 16)
		v = uint16(val.(uint64))
	case reflect.Uint32:
		val, err = strconv.ParseUint(value, 10, 32)
		v = uint32(val.(uint64))
	case reflect.Uint64:
		v, err = strconv.ParseUint(value, 10, 64)

	case reflect.Float32:
		val, err = strconv.ParseFloat(value, 32)
		v = float32(val.(float64))
	case reflect.Float64:
		v, err = strconv.ParseFloat(value, 64)

	case reflect.String:
		v, err = value, nil
	}

	if err != nil {
		return nil, err
	}
	return v, nil
}

const (
	qnameTimeDuration = "time.Duration"
)

func qualName(t reflect.Type) string {
	return fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
}

func parseInt64(t reflect.Type, value string) (v interface{}, err error) {
	switch qualName(t) {
	case qnameTimeDuration:
		v, err = time.ParseDuration(value)
	default: // fallback to "int64"
		v, err = strconv.ParseInt(value, 10, 64)
	}

	return
}
